/**
 * 
 */
package cn.cityhouse.avm.utils;

import java.io.FileReader;
import java.io.IOException;
import java.util.List;

import org.apache.commons.math3.exception.NoDataException;
import org.apache.commons.math3.exception.NullArgumentException;
import org.apache.commons.math3.linear.Array2DRowRealMatrix;
import org.apache.commons.math3.linear.RealMatrix;

import com.opencsv.CSVReader;

/**
 * @author <a href="mailto:wangcl@lreis.ac.cn">ChenLiang Wang</a>
 *
 */
public class DataFrame {

	public static void print(DataFrame df) {
		if (df.checkDataReady()) {
			for (int i = 0; i < df.getNrow(); i++) {
				if (i >= Out.getN_max_print_row()) {
					System.out.println("[ reached max.print -- omitted " + (df.getNrow() - i) + " entries ]");
					break;
				}
				Out.printArr(df.data_matrix.getRow(i));
			}
		}
	}

	public static void print(DataFrame df, double[][] d_data) {
		if (df.checkDataReady() && d_data != null && d_data[0].length > 0) {
			for (int i = 0; i < df.getNrow(); i++) {
				if (i >= Out.getN_max_print_row()) {
					System.out.println("[ reached max.print -- omitted " + (df.getNrow() - i) + " entries ]");
					break;
				}
				Out.printArr(d_data[i]);
			}
		}
	}

	/**
	 * 
	 */
	private double[][] d_data = null;
	private RealMatrix data_matrix = null;
	private int nrow = 0;
	private int ncol = 0;
	private String[] str_colnames = null;
	private String str_ds = null;

	private boolean isMem = false;
	private boolean isVerbose = false;

	public DataFrame() {
		// TODO Auto-generated constructor stub
	}

	public boolean checkDataReady() {
		return (getNcol() >= 0 && getNrow() >= 0 && getD_data() != null && getData_matrix() != null
				&& getStr_colnames() != null && getStr_ds() != null);
	}

	/**
	 * @param str_col_name
	 *            column name
	 * @return return the selected column arrary
	 */
	public double[] getColumn(String str_col_name) {
		if (str_col_name == null) {
			throw new NullArgumentException();
		}
		for (int i = 0; i < str_colnames.length; i++) {// find the index of
			// selected columns
			if (str_col_name.trim().equals(str_colnames[i].trim())) {
				return (data_matrix.getColumn(i));// find y
			}
		}
		return null;
	}

	public double[][] getColumns(String[] str_col_names) {
		if (!checkDataReady()) {
			throw new NullPointerException("Error in loading data!!! PLS check it again!");
		}
		int[] indices_col = new int[str_col_names.length];
		int[] indices_row = new int[getNrow()];
		for (int i = 0; i < getNrow(); i++) {
			indices_row[i] = i;
		}
		for (int i = 0; i < str_col_names.length; i++) {
			for (int j = 0; j < getStr_colnames().length; j++) {// find the
				// index of
				// selected columns
				if (getStr_colnames()[j].trim().equals(str_col_names[i].trim())) {
					indices_col[i] = j;// find all index for x
				}
			}
		}
		return (data_matrix.getSubMatrix(indices_row, indices_col).getData());
	}

	public double[][] getD_data() {
		return d_data;
	}

	public RealMatrix getData_matrix() {
		return data_matrix;
	}

	public int getNcol() {
		ncol = this.data_matrix.getColumnDimension();
		return ncol;
	}

	public int getNrow() {
		nrow = this.data_matrix.getRowDimension();
		return nrow;
	}

	public String[] getStr_colnames() {
		return str_colnames;
	}

	public String getStr_ds() {
		return str_ds;
	}

	public boolean isMem() {
		return isMem;
	}

	public boolean isVerbose() {
		return isVerbose;
	}

	/**
	 * @param str_ds_csv
	 *            String to indicate data source of CSV Data
	 * @return 2D double Array in CSV
	 */
	public double[][] readCsv(String str_ds_csv) {
		if (str_ds_csv != null && str_ds_csv.length() <= 0) {
			throw new NoDataException();
		}
		CSVReader csv_reader = null;
		List<String[]> myEntriesLine = null;
		this.str_ds = str_ds_csv;
		try {
			csv_reader = new CSVReader(new FileReader(str_ds_csv));
			myEntriesLine = csv_reader.readAll();
			csv_reader.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			System.exit(-1);
		}
		this.str_colnames = myEntriesLine.get(0);
		this.ncol = this.str_colnames.length;
		myEntriesLine.remove(0);
		this.nrow = myEntriesLine.size();
		String[][] dataArr = new String[this.nrow][];
		dataArr = myEntriesLine.toArray(dataArr);
		this.d_data = new double[this.nrow][];
		if (this.isVerbose) {
			System.out.println("The data source: " + this.str_ds);
			System.out.println("the length of col=" + this.str_colnames.length);
			System.out.println("the length of row=" + this.nrow);
		}
		for (int i = 0; i < this.nrow; i++) {
			d_data[i] = new double[this.ncol];
			for (int j = 0; j < this.ncol; j++) {
				try {
					d_data[i][j] = Double.parseDouble(dataArr[i][j]);
				} catch (NumberFormatException e) {
					// TODO Auto-generated catch block
					if (i < Out.getN_max_print_row()) {
						System.out.print("Warning : NumberFormatException\t");
						System.out.print("i=" + String.valueOf(i) + " ,j=" + String.valueOf(j));
						System.out.println(" " + String.valueOf(d_data[i][j]));

					} else {
						if (i == Out.getN_max_print_row()) {
							System.out.println("reached max.print -- omitted " + (this.nrow - i) + " entries");
						}
					}
					// e.printStackTrace();
				}
			}
		}
		this.data_matrix = new Array2DRowRealMatrix(d_data);
		return d_data;
	}

	public void setD_data(double[][] d_data) {
		this.d_data = d_data;
	}

	public void setData_matrix(RealMatrix data_matrix) {
		this.data_matrix = data_matrix;
		this.nrow = this.getNrow();
		this.ncol = this.getNcol();
	}

	public void setMem(boolean isMem) {
		this.isMem = isMem;
	}

	public void setStr_colnames(String[] str_colnames) {
		this.str_colnames = str_colnames;
	}

	public void setStr_ds(String str_ds) {
		this.str_ds = str_ds;
	}

	public void setVerbose(boolean isVerbose) {
		this.isVerbose = isVerbose;
	}

}
